<template>
  <div>
    <el-dialog
      :title="title"
      v-model="dialogVisible"
      :width="width"
      append-to-body
      :close-on-click-modal="false"
      @close="close"
    >
      <div class="bind">
        <div class="bind-left">
          <div v-if="showSearch" class="search">
            <el-input
              v-model="queryParams[searchProp]"
              :placeholder="searchPlaceholder"
              :prefix-icon="Search"
              @keyup.enter="handleQuery"
            />
            <el-button type="primary" size="small" style="margin-left: 20px;" @click="handleQuery">搜索</el-button>
          </div>
          <div class="bind-tab">
            <div class="table" :style="{width: typeof tableWidth === 'number' ? tableWidth + 'px' : tableWidth}">
              <el-scrollbar :style="{height: typeof height === 'number' ? height + 'px' : height}" class="bind-scroll">
                <el-table
                  ref="tableRef"
                  v-loading="loading"
                  :data="tableData"
                  border
                  tooltip-effect="dark"
                  style="width: 100%;"
                  :row-key="row => row.id"
                  @selection-change="handleTableSelect"
                  @row-click="handleTableRowClick"
                >
                  <el-table-column type="selection" width="50" align="center" :reserve-selection="true" />
                  <slot name="default" />
                </el-table>
              </el-scrollbar>
              <el-pagination
                v-show="total > 0"
                v-model:current-page="queryParams.pageIndex"
                v-model:page-size="queryParams.pageSize"
                :total="total"
                :page-sizes="[5, 10, 20]"
                layout="total, sizes, prev, pager, next, jumper"
                @size-change="getData"
                @current-change="getData"
              />
            </div>
          </div>
        </div>
        <div class="selection">
          <div class="selection-item">已选{{ bindTitle }}({{ selections.length }})</div>
          <el-scrollbar :style="{height: typeof height === 'number' ? height + 'px' : height}" class="bind-scroll">
            <div v-for="(selection, idx) in selections" :key="selection[valueProp]" class="selection-item">
              <div class="name">
                <slot name="selection" :selection="selection">
                  {{ selection.name }}
                </slot>
              </div>
              <div class="delete el-icon-delete" @click="deleteSelection(selection, idx)" />
            </div>
          </el-scrollbar>
        </div>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary" @click="submit">确 定</el-button>
          <el-button @click="close">取 消</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { Search } from '@element-plus/icons-vue'
import modal from "@/plugins/modal.js"

const props = defineProps({
  title: { type: String, default: '' },
  show: { type: Boolean, default: false },
  width: { type: [String, Number], default: '960px' },
  height: { type: [String, Number], default: '520px' },
  tableWidth: { type: [String, Number], default: '620px' },
  loadDataApi: { type: String, required: true },
  dataExtraParam: { type: Object, default: () => ({}) },
  loadSelectionApi: { type: String, default: '' },
  valueProp: { type: [String, Number], default: 'id' },
  dataProp: { type: String, default: 'records' },
  bindTitle: { type: String, default: '' },
  bindProp: { type: String, required: true },
  bindValue: { type: [String, Number], required: true },
  showSearch: { type: Boolean, default: true },
  searchPlaceholder: { type: String, default: '请输入关键字搜索' },
  searchProp: { type: String, default: 'keywords' },
  submitImmediateClose: { type: Boolean, default: false }
})

const emit = defineEmits(['update:show', 'save', 'close'])

const dialogVisible = computed({
  get: () => props.show,
  set: (value) => emit('update:show', value)
})

const loading = ref(true)
const queryParams = ref({
  pageIndex: 1,
  pageSize: 10,
  keywords: ''
})
const tableData = ref([])
const total = ref(0)
const ifTriggerSelection = ref(false)
const selectionLoaded = ref(false)
const selections = ref([])
const tableRef = ref(null)

watch(() => props.show, (newVal, oldVal) => {
  if (newVal !== oldVal && newVal) {
    selectionLoaded.value = false
    handleQuery()
  }
})

const getData = async () => {
  loading.value = true
  $xl.apis[props.loadDataApi]({
      params: {
        ...queryParams.value,
        ...props.dataExtraParam
      }
    }).then(res => {
      loading.value = false
      tableData.value = res.data[props.dataProp]
      total.value = typeof res.data.total === 'number' ? res.data.total : parseInt(res.data.total)
      getSelection()
    }).catch(err => {
      loading.value = false
      modal.msgError(err)
    })
}

const getSelection = () => {
  ifTriggerSelection.value = false
  tableRef.value.clearSelection()

  if (!selectionLoaded.value && props.loadSelectionApi) {
      $xl.apis[props.loadSelectionApi]({
        params: {
          [props.bindProp]: props.bindValue
        }
      }).then(res => {
        selections.value = []
        if (res.data?.length) {
          res.data.forEach(data => {
            selections.value.push(data)
            const selection = tableData.value.find(v => v[props.valueProp] === data[props.valueProp])
            selection && tableRef.value.toggleRowSelection(selection)
          })
        }
        selectionLoaded.value = true
        ifTriggerSelection.value = true
      }).catch(err => {
        console.log(err)
      })
  } else {
    nextTick()
    const filter = tableData.value.filter(v => selections.value.some(data => v[props.valueProp] === data[props.valueProp]))
    if (filter.length) {
      filter.forEach(v => tableRef.value.toggleRowSelection(v))
    }
    ifTriggerSelection.value = true
  }
}

const handleQuery = () => {
  queryParams.value.pageIndex = 1
  getData()
}

const handleTableSelect = (selection) => {
  if (ifTriggerSelection.value) {
    selection.forEach(select => {
      if (!selections.value.some(v => v[props.valueProp] === select[props.valueProp])) {
        selections.value.push(select)
      }
    })
    selections.value = selections.value.filter(v =>
        selection.some(d => d[props.valueProp] === v[props.valueProp]) || !tableData.value.some(d => d[props.valueProp] === v[props.valueProp])
    )
  }
}

const handleTableRowClick = (row) => {
  tableRef.value.toggleRowSelection(row)
}

const deleteSelection = (selection, idx) => {
  selections.value.splice(idx, 1)
  const row = tableData.value.find(v => v[props.valueProp] === selection[props.valueProp])
  row && tableRef.value.toggleRowSelection(row)
}

const submit = () => {
  emit('save', selections.value, close)
  if (props.submitImmediateClose) {
    close()
  }
}

const close = () => {
  tableRef.value.clearSelection()
  dialogVisible.value = false
  emit('close')
}
</script>

<style lang="scss" scoped>
.bind {
  display: flex;
  height: 720px;

  &-left {
    .search {
      display: flex;
      align-items: center;
      margin-bottom: 30px;
    }
  }

  &-tab {
    display: flex;

    :deep(.el-tabs__item) {
      width: 90px;
      padding-right: 30px;
    }
  }

  .selection {
    flex: 1;
    margin-left: 30px;
    overflow: hidden;

    &-item {
      box-sizing: border-box;
      padding: 14px 20px;
      border: 1px solid #f6f6f6;
      color: #666;
      display: flex;
      align-items: center;
      justify-content: space-between;

      .name {
        font-size: 18px;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        flex: 1;
      }

      .delete {
        cursor: pointer;
        color: #F56C6C;
        margin-left: 20px;
      }
    }
  }
}

:deep(.bind-scroll .el-scrollbar__wrap) {
  overflow-x: hidden;
}

:deep(.el-dialog__body) {
  padding-top: 10px;
}
</style>
